﻿using Hx.ADSyncPlatform.ActiveDirectory.Access;
using Hx.ADSyncPlatform.ActiveDirectory.Extension;
using Hx.ADSyncPlatform.ActiveDirectory.Model;
using Hx.ADSyncPlatform.ActiveDirectory.Service.Base;
using Hx.ADSyncPlatform.Entity.Entities;
using Hx.ADSyncPlatform.Infrastructure.Exceptions;
using Hx.ADSyncPlatform.Infrastructure.Page;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Linq;
using System.Text;

namespace Hx.ADSyncPlatform.ActiveDirectory.Service
{
    /// <summary>
    /// 计算机
    /// </summary>
    public class ComputerService : BaseService
    {

        private ADConnection ADCInfo;
        public ComputerService(ADConnection _adcInfo)
        {
            this.ADCInfo = _adcInfo;
        }


        /// <summary>
        /// 单个计算机
        /// </summary>
        /// <param name="sAMAccountName">账户名</param>
        /// <returns>单个计算机信息</returns>
        public Computer Single(string sAMAccountName)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var computer = new ComputerAccess().Single(adc.RootEntry, sAMAccountName);
                return computer == null ? null : computer.Properties?.ConvertTo<Computer>();
            }
        }

        public Computer Info(string sAMAccountName)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var sr = new ComputerAccess().Single(adc.RootEntry, sAMAccountName);
                var computer = sr == null ? null : sr.Properties?.ConvertTo<Computer>();
                if (computer != null)
                {
                    SearchResultCollection src = new ComputerAccess().BindUsers(adc.RootEntry, sAMAccountName);
                    List<Person> bindUsers = src.OfType<SearchResult>().Select(n =>
                    {
                        return n.Properties.ConvertTo<Person>();
                    }).ToList();
                    computer.BindUsers = bindUsers;
                }

                return computer;
            }
        }

        /// <summary>
        /// 查询组织下的计算机(导出专用)
        /// </summary>
        /// <param name="orgCn">组织CN</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns>计算机集合</returns>
        public List<Computer> Get(string dn, string cn, string name, List<Plat_DataPermission> dataPermissions, List<string> systemOus, string searchScope = "current")
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                SearchResultCollection src = null;
                IEnumerable<SearchResult> searchResults = null;
                if (!string.IsNullOrEmpty(dn))
                {
                    DirectoryEntry org = null;
                    var orgDe = new OrganizationAccess().SingleByDistinguishedName(adc.RootEntry, dn);
                    if (orgDe == null)
                    {
                        throw new ActiveDirectoryObjectNotFoundException($"组织【{dn}】不存在，查询无法继续。");
                    }
                    org = orgDe.GetDirectoryEntry();

                    if (searchScope == "current")
                    {
                        var subCn = new OrganizationAccess().Single(org, cn, SearchScope.OneLevel);
                        if (subCn == null) return null;
                        src = new ComputerAccess().Get(subCn.GetDirectoryEntry(), SearchScope.OneLevel, name);
                    }
                    else if (searchScope == "global")
                    {
                        src = new ComputerAccess().Get(adc.RootEntry, SearchScope.Subtree, name);
                    }

                }
                else
                {
                    src = new ComputerAccess().Get(adc.RootEntry, SearchScope.Subtree, name);
                }
                if (searchResults == null) searchResults = src.OfType<SearchResult>();

                if (base.filterObjectByDataPermission != null)
                {
                    searchResults = filterObjectByDataPermission(searchResults, dataPermissions, systemOus);
                }
                List<Computer> data = new List<Computer>();
                if (src == null || src.Count == 0)
                {
                    return data;
                }
                foreach (SearchResult computerDe in src)
                {
                    var item = computerDe?.Properties?.ConvertTo<Computer>();
                    if (item != null)
                    {
                        data.Add(item);
                    }
                }
                return data;


            }
        }


        /// <summary>
        /// ou下的私有计算机
        /// </summary>
        /// <param name="orgCn">组织OU</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns>计算机集合</returns>
        public List<Computer> PrivateComputers(string ou, string targetou, SearchScopeDerivation searchScope, string info)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                DirectoryEntry org = null;
                //hack：new xxxx(【xx.RootEntry】)这种写法感觉很怪异
                var orgDe = new OrganizationAccess().SingleByDistinguishedName(adc.RootEntry, ou);
                if (orgDe == null)
                {
                    //hack:此处用NullReferenceException这个异常不合适，可以考虑如下：
                    /*
                     * System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectExistsException 类异常引发时创建 Active Directory 域服务对象以及该对象已存在于基础目录存储区。
                     * System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectNotFoundException 类异常在基础目录存储区中找不到请求的对象时引发。
                     * 还有一些.NET提供的异常，都可以参考。
                     */
                    return new List<Computer>();
                }
                org = orgDe.GetDirectoryEntry();

                //var computerDes = new ComputerAccess().GetByInfo(org, searchScope.Convert(), info);
                var computerDes = new ComputerAccess().Get(org, searchScope.Convert());
                List<Computer> data = new List<Computer>();
                if (computerDes == null || computerDes.Count == 0)
                {
                    return data;
                }

                foreach (SearchResult computerDe in computerDes)
                {
                    var item = computerDe?.Properties?.ConvertTo<Computer>();
                    if (item != null)
                    {
                        data.Add(item);
                    }
                }
                return data;
            }
        }

        /// <summary>
        /// 查询计算机 分页
        /// </summary>
        /// <param name="orgCn">组织CN</param>
        /// <param name="name">显示名</param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns>计算机分页集合</returns>
        public PageResult<Computer> PageList(string dn, ConfigItem configItem, string name, List<Plat_DataPermission> dataPermissions, List<string> systemOus, string searchScope = "current", int pageIndex = 1, int pageSize = 10)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                SearchResultCollection src = null;
                IEnumerable<SearchResult> searchResults = null;
                if (!string.IsNullOrEmpty(dn))
                {
                    DirectoryEntry org = null;
                    var orgDe = new OrganizationAccess().SingleByDistinguishedName(adc.RootEntry, dn);
                    if (orgDe == null)
                    {
                        throw new ActiveDirectoryObjectNotFoundException($"组织【{dn}】不存在，查询无法继续。");
                    }
                    org = orgDe.GetDirectoryEntry();

                    if (searchScope == "current")
                    {
                        var subCn = new OrganizationAccess().Single(org, configItem.cn, SearchScope.OneLevel);
                        if (subCn == null)
                            return new PageResult<Computer>(new List<Computer>(), pageIndex, 0);
                        src = new ComputerAccess().Get(subCn.GetDirectoryEntry(), SearchScope.OneLevel, name);
                        searchResults = src.OfType<SearchResult>();
                    }
                    else if (searchScope == "global")
                    {
                        src = new ComputerAccess().Get(adc.RootEntry, SearchScope.Subtree, name);
                        searchResults = src.OfType<SearchResult>()
                            .Where(n => n.Properties["DistinguishedName"][0].ToString().Contains($"OU={configItem.name}"));
                    }

                }
                else
                {
                    src = new ComputerAccess().Get(adc.RootEntry, SearchScope.Subtree, name);
                }
                if (searchResults == null) searchResults = src.OfType<SearchResult>();

                if (base.filterObjectByDataPermission != null)
                {
                    searchResults = filterObjectByDataPermission(searchResults, dataPermissions, systemOus);
                }
                int total = searchResults.Count();
                int startNum = Math.Max(pageIndex - 1, 0) * pageSize;
                searchResults = searchResults.Skip(startNum).Take(pageSize);


                var computers = searchResults.Select(n =>
                {
                    return n.Properties?.ConvertTo<Computer>();

                }).ToList();

                PageResult<Computer> pageResult = new PageResult<Computer>(computers, pageIndex, total);
                return pageResult;



            }
        }


        /// <summary>
        /// 查询公共计算机 分页
        /// </summary>
        /// <param name="orgCn">组织CN</param>
        /// <param name="name">显示名</param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns>计算机分页集合</returns>
        public PageResult<Computer> PublicComputersPageList(string distinguishedName, string name, string searchVal, string userWorkStation, bool? binded = null, int pageIndex = 1, int pageSize = 10)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                SearchResultCollection src = null;

                if (!string.IsNullOrEmpty(distinguishedName))
                {
                    DirectoryEntry org = null;
                    var orgDe = new OrganizationAccess().SingleByDistinguishedName(adc.RootEntry, distinguishedName);
                    if (orgDe == null)
                    {
                        return PageResult<Computer>.Default();
                        //Organization organization = new Organization()
                        //{
                        //    CN = configItem.cn,
                        //    Name = configItem.name,
                        //    DisplayName = configItem.name,
                        //    Ou = configItem.name

                        //};
                        //string parentOrgDN = string.Join(",", distinguishedName.Split(",").Skip(1));
                        //var parentOrg = new OrganizationAccess().SingleByDistinguishedName(adc.RootEntry, parentOrgDN);
                        //new OrganizationAccess().Create(parentOrg.GetDirectoryEntry(), organization);
                        //orgDe = new OrganizationAccess().SingleByDistinguishedName(parentOrg.GetDirectoryEntry(), distinguishedName);
                        //throw new ActiveDirectoryObjectNotFoundException($"组织【{distinguishedName}】不存在，查询无法继续。");
                    }
                    org = orgDe.GetDirectoryEntry();
                    string filter = "(&(objectclass=Computer)";
                    if (binded.HasValue)
                    {
                        string[] computerNames = userWorkStation.Split(",");
                        StringBuilder sb = new StringBuilder();

                        if (binded.Value == true)
                        {
                            if (string.IsNullOrWhiteSpace(userWorkStation)) return PageResult<Computer>.Default();
                            sb.Append("(|");
                            foreach (string computerName in computerNames)
                            {
                                sb.Append($"(cn={computerName})");
                            }
                        }
                        else
                        {
                            sb.Append("(&");
                            foreach (string computerName in computerNames)
                            {
                                sb.Append($"(!(cn={computerName}))");
                            }
                        }
                        sb.Append(")");
                        filter = $"{filter}{sb}";
                    }
                    if (searchVal != "")
                    {
                        filter = $"(&{filter}(sAMAccountName=*{searchVal}*))";

                    }
                    filter += ")";
                    src = new ComputerAccess().Search(org, filter, null, SearchScope.OneLevel, 2000);
                    //else
                    //{
                    //    src = new ComputerAccess().Get(org, SearchScope.OneLevel, name);
                    //}

                }

                else
                {
                    src = new ComputerAccess().Get(adc.RootEntry, SearchScope.Subtree, name);
                }

                int total = src.Count;
                int startNum = Math.Max(pageIndex - 1, 0) * pageSize;
                var searchResults = src.OfType<SearchResult>().Skip(startNum).Take(pageSize);

                var computers = searchResults.Select(n =>
                {
                    return n.Properties?.ConvertTo<Computer>();

                }).ToList();

                PageResult<Computer> pageResult = new PageResult<Computer>(computers, pageIndex, total);
                return pageResult;



            }
        }


        /// <summary>
        /// 查询公共计算机 
        /// </summary>
        /// <param name="distinguishedName">组织DN</param>
        /// <param name="name">显示名</param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns>计算机分页集合</returns>
        public List<Computer> PublicComputers(string distinguishedName, ConfigItem configItem)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                SearchResultCollection src = null;

                DirectoryEntry org = null;
                var orgDe = new OrganizationAccess().SingleByDistinguishedName(adc.RootEntry, distinguishedName);
                if (orgDe == null)
                {
                    return new List<Computer>();
                }
                org = orgDe.GetDirectoryEntry();
                src = new ComputerAccess().Get(org, SearchScope.OneLevel, "");

                var searchResults = src.OfType<SearchResult>();

                var computers = searchResults.Select(n =>
                {
                    return n.Properties?.ConvertTo<Computer>();

                }).ToList();

                return computers;


            }
        }

        /// <summary>
        /// 用户的私人计算机列表
        /// </summary>
        /// <param name="orgCn">组织CN</param>
        /// <param name="name">显示名</param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns>计算机分页集合</returns>
        public PageResult<Computer> PersonPrivateComputers(string distinguishedName, string personName, string searchVal = "", string userWorkStation = "", bool? binded = null, int pageIndex = 1, int pageSize = 10)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                SearchResultCollection src = null;

                DirectoryEntry org = null;
                var orgDe = new OrganizationAccess().SingleByDistinguishedName(adc.RootEntry, distinguishedName);
                if (orgDe == null)
                {
                    return PageResult<Computer>.Default();
                    //throw new ActiveDirectoryObjectNotFoundException($"组织【{distinguishedName}】不存在，查询无法继续。");
                }
                org = orgDe.GetDirectoryEntry();
                if (personName.Length > 12) personName = personName.Substring(0, 12);
                List<string> prefixs = new string[] { "hd", "ds", "pd", "jxl" }.ToList();
                StringBuilder sb1 = new StringBuilder();
                prefixs.ForEach(n =>
                {
                    sb1.Append($"(name={n}*)");
                });
                string filter = string.Format("(&(objectclass=Computer)(|(sAMAccountName={0}*){1})", personName.ToUpper() + "c", sb1.ToString());
                if (binded.HasValue)
                {
                    string[] computerNames = userWorkStation.Split(",");
                    StringBuilder sb = new StringBuilder();
                    if (binded.Value == true)
                    {
                        if (string.IsNullOrWhiteSpace(userWorkStation)) return PageResult<Computer>.Default();
                        sb.Append("(|");
                        foreach (string computerName in computerNames)
                        {
                            sb.Append($"(cn={computerName})");
                        }
                    }
                    else
                    {
                        sb.Append("(&");
                        foreach (string computerName in computerNames)
                        {
                            sb.Append($"(!(cn={computerName}))");
                        }
                    }
                    sb.Append(")");
                    filter = $"{filter}{sb}";
                }

                if (searchVal != "")
                {
                    filter = string.Format($"(&{filter}(cn=*{searchVal}*))");
                }
                filter += ")";
                src = new ComputerAccess().Search(org, filter, null, SearchScope.OneLevel, 2000);
                int total = src.Count;
                int startNum = Math.Max(pageIndex - 1, 0) * pageSize;
                var searchResults = src.OfType<SearchResult>().Skip(startNum).Take(pageSize);

                var computers = searchResults.Select(n =>
                {
                    return n.Properties?.ConvertTo<Computer>();

                }).ToList();

                PageResult<Computer> pageResult = new PageResult<Computer>(computers, pageIndex, total);
                return pageResult;



            }
        }

        public List<Computer> PersonPrivateComputers(string distinguishedName, string personName, string searchVal = "", string userWorkStation = "", bool? binded = null)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                SearchResultCollection src = null;

                DirectoryEntry org = null;
                var orgDe = new OrganizationAccess().SingleByDistinguishedName(adc.RootEntry, distinguishedName);
                if (orgDe == null)
                {
                    return new List<Computer>();
                    //throw new ActiveDirectoryObjectNotFoundException($"组织【{distinguishedName}】不存在，查询无法继续。");
                }
                org = orgDe.GetDirectoryEntry();
                if (personName.Length > 12) personName = personName.Substring(0, 12);
                List<string> prefixs = new string[] { "hd", "ds", "pd", "jxl" }.ToList();
                StringBuilder sb1 = new StringBuilder();
                prefixs.ForEach(n =>
                {
                    sb1.Append($"(name={n}*)");
                });
                string filter = string.Format("(&(objectclass=Computer)(|(name={0})(sAMAccountName={1}*){2})", personName, personName.ToUpper() + "c", sb1.ToString());
                if (binded.HasValue)
                {
                    string[] computerNames = userWorkStation.Split(",");
                    StringBuilder sb = new StringBuilder();
                    if (binded.Value == true)
                    {
                        if (string.IsNullOrWhiteSpace(userWorkStation)) return new List<Computer>();
                        sb.Append("(|");
                        foreach (string computerName in computerNames)
                        {
                            sb.Append($"(cn={computerName})");
                        }
                    }
                    else
                    {
                        sb.Append("(&");
                        foreach (string computerName in computerNames)
                        {
                            sb.Append($"(!(cn={computerName}))");
                        }
                    }
                    sb.Append(")");
                    filter = $"{filter}{sb}";
                }

                if (searchVal != "")
                {
                    filter = string.Format($"(&{filter}(cn=*{searchVal}*))");
                }
                filter += ")";
                src = new ComputerAccess().Search(org, filter, null, SearchScope.OneLevel, 2000);

                var computers = src.OfType<SearchResult>().Select(n =>
              {
                  return n.Properties?.ConvertTo<Computer>();

              }).ToList();

                return computers;



            }
        }
        /// <summary>
        ///自定义查询
        /// </summary>
        /// <param name="filter">LDAP查询语句</param>
        /// <param name="propertiesToLoad">需要返回的属性</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="pagination">分页信息，NULL时返回所有</param>
        /// <returns>计算机集合以及分页信息</returns>
        public PageResult<Computer> Search(string filter, List<string> propertiesToLoad, SearchScopeDerivation searchScope, int pageIndex = 1, int pageSize = 10)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var src = new ComputerAccess().Search(adc.RootEntry, filter, propertiesToLoad, searchScope.Convert(), pageIndex * pageSize);
                int total = src.Count;
                int startNum = Math.Max(pageIndex - 1, 0) * pageSize;
                var searchResults = src.OfType<SearchResult>().Skip(startNum).Take(pageSize).ToList();

                var computerList = searchResults.Select(n =>
                {
                    return n.Properties?.ConvertTo<Computer>();

                }).ToList();

                PageResult<Computer> pageResult = new PageResult<Computer>(computerList, pageIndex, total);
                return pageResult;
            }
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="sAMAccountName">需要删除的计算机名称</param>
        /// <returns>成功True，失败False</returns>
        public bool Delete(string sAMAccountName)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new ComputerAccess();
                var computerDes = access.Single(adc.RootEntry, sAMAccountName);
                if (computerDes == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"计算机【{sAMAccountName}】不存在，无法删除。");
                }
                return access.Delete(computerDes.GetDirectoryEntry());
            }
        }

        /// <summary>
        /// 移动
        /// </summary>
        /// <param name="orgCn">移动的目标组织CN</param>
        /// <param name="computerName">需要移动的计算机名称</param>
        /// <returns>成功True，失败False</returns>
        public bool Move(string orgCn, string sAMAccountName)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                DirectoryEntry org = null;
                var orgDe = new OrganizationAccess().Single(adc.RootEntry, orgCn);
                if (orgDe == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"组织【{orgCn}】不存在，移动无法继续。");
                }
                org = orgDe.GetDirectoryEntry();

                var access = new ComputerAccess();
                var computerDes = access.Single(adc.RootEntry, sAMAccountName);
                if (computerDes == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"计算机【{sAMAccountName}】不存在，移动计算机无法继续。");
                }
                return access.Move(org, computerDes.GetDirectoryEntry());
            }
        }

        public bool Create(string orgCn, Computer computer)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                DirectoryEntry org = null;
                var orgDe = new OrganizationAccess().Single(adc.RootEntry, orgCn);
                if (orgDe == null)
                {
                    throw new ActiveDirectoryObjectNotFoundException($"组织【CN:{orgCn}】不存在，创建计算机请求无法继续。");
                }

                org = orgDe.GetDirectoryEntry();
                var findOne = new ComputerAccess().Single(org, computer.SamAccountName);
                if (findOne != null) throw new BizException($"组织下已存在计算机【{computer.DisplayName}】");
                var result = new ComputerAccess().Create(org, computer);
                return result;
            }
        }
        public bool CreateComputer(string dn, Computer computer, ConfigItem config)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                DirectoryEntry org = null;
                var orgDe = new OrganizationAccess().SingleByDistinguishedName(adc.RootEntry, dn);
                if (orgDe == null)
                {
                    throw new ActiveDirectoryObjectNotFoundException($"组织【DN:{dn}】不存在，创建计算机请求无法继续。");
                }
                org = orgDe.GetDirectoryEntry();
                //var computerOrg = new OrganizationAccess().SingleByOU(org, targetOu);
                var computerOrg = new OrganizationAccess().GetBrotherOrg(org, config.name);
                if (computerOrg == null)//组织不存在 创建组织
                {
                    var newOrg = new Organization();
                    newOrg.CN = config.cn;
                    newOrg.Name = config.name;
                    newOrg.Ou = config.name; ;
                    new OrganizationAccess().Create(org, newOrg);
                    computerOrg = new OrganizationAccess().GetBrotherOrg(org, config.name);
                }
                var findOne = new ComputerAccess().Single(computerOrg, computer.SamAccountName);
                if (findOne != null) throw new BizException($"组织下已存在计算机【{computer.Name}】");
                var result = new ComputerAccess().Create(computerOrg, computer);
                return result;
            }
        }
        public bool Update(Computer computer)
        {
            using (var adc = new ADConnection(ADCInfo.LDAPPath, ADCInfo.UserName, ADCInfo.Password))
            {
                var access = new ComputerAccess();
                var computerDes = access.Single(adc.RootEntry, computer.SamAccountName);
                if (computerDes == null)
                {
                    //hack：此处用NullReferenceException不合适
                    throw new ActiveDirectoryObjectNotFoundException($"计算机【{computer.SamAccountName}】不存在，无法修改。");
                }

                return access.Update(computerDes.GetDirectoryEntry(), computer);
            }
        }
    }
}
